package com.zc.component.dashboard.web;

import com.alibaba.fastjson.JSON;

import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.ReactiveResult;
import com.zc.common.core.websocket.WebSocketService;
import com.zc.common.core.websocket.constant.WebSocketEvent;
import com.zc.component.dashboard.DashboardManager;
import com.zc.component.dashboard.DashboardObject;
import com.zc.component.dashboard.MeasurementParameter;
import com.zc.component.dashboard.web.request.DashboardMeasurementRequest;
import com.zc.component.dashboard.web.response.DashboardInfo;
import com.zc.component.dashboard.web.response.DashboardMeasurementResponse;
import com.zc.component.dashboard.web.response.MeasurementInfo;
import com.zc.web.exception.NotFoundException;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;

import static com.ruoyi.common.utils.SecurityUtils.getLoginUser;

/**
 * 仪表盘
 */
@RestController
@RequestMapping("/dashboard")
public class DashboardController {

    private final DashboardManager dashboardManager;

    public DashboardController(DashboardManager dashboardManager) {
        this.dashboardManager = dashboardManager;
    }

    @GetMapping("/defs")
    public AjaxResult getDefinitions(@RequestHeader("Message-ID") String messageId) {

        String token = getLoginUser().getToken();

        Flux<DashboardInfo> result = dashboardManager
                .getDashboards()
                .flatMap(DashboardInfo::of);

        result.collectList().subscribe(dashboardInfo -> WebSocketService.postEvent(token, WebSocketEvent.REACTIVE, ReactiveResult.result(messageId, dashboardInfo)));

        return AjaxResult.success();
    }

    @GetMapping("/def/{dashboard}/{object}/measurements")
    public AjaxResult getMeasurementDefinitions(
            @RequestHeader("Message-ID") String messageId,
            @PathVariable String dashboard,
            @PathVariable String object) {

        String token = getLoginUser().getToken();

        Flux<MeasurementInfo> result = dashboardManager
                .getDashboard(dashboard)
                .flatMap(dash -> dash.getObject(object))
                .flatMapMany(DashboardObject::getMeasurements)
                .flatMap(MeasurementInfo::of);

        result.collectList().subscribe(measurementInfo -> WebSocketService.postEvent(token, WebSocketEvent.REACTIVE, ReactiveResult.result(messageId, measurementInfo)));
        return AjaxResult.success();
    }

    @GetMapping(value = "/{dashboard}/{object}/{measurement}/{dimension}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public AjaxResult getMeasurementValue(
            @RequestHeader("Message-ID") String messageId,
            @PathVariable String dashboard,
            @PathVariable String object,
            @PathVariable String dimension,
            @PathVariable String measurement,
            @RequestParam Map<String, Object> params) {

        String token = getLoginUser().getToken();

        Flux<Object> result = dashboardManager
                .getDashboard(dashboard)
                .flatMap(dash -> dash.getObject(object))
                .flatMap(obj -> obj.getMeasurement(measurement))
                .flatMap(meas -> meas.getDimension(dimension))
                .switchIfEmpty(Mono.error(() -> new NotFoundException("不支持的仪表盘")))
                .flatMapMany(dim -> dim.getValue(MeasurementParameter.of(params)));

        result.collectList().subscribe(data -> WebSocketService.postEvent(token, WebSocketEvent.REACTIVE, ReactiveResult.result(messageId, data)));
        return AjaxResult.success();
    }

    /**
     * POST 方式批量获取仪表数据,不支持获取实时数据.
     *
     * @param requests 请求参数
     * @return 仪表数据
     */
    @PostMapping(value = "/_multi")
    public AjaxResult getMultiMeasurementValue(
            @RequestHeader("Message-ID") String messageId,
            @RequestBody List<DashboardMeasurementRequest> requests) {
        String token = getLoginUser().getToken();

        Flux<DashboardMeasurementResponse> result = Flux.fromIterable(requests).flatMap(request -> dashboardManager
                .getDashboard(request.getDashboard())
                .flatMap(dash -> dash.getObject(request.getObject()))
                .flatMap(obj -> obj.getMeasurement(request.getMeasurement()))
                .flatMap(meas -> meas.getDimension(request.getDimension()))
                .filter(dim -> !dim.isRealTime()) //实时数据请使用EventSource方式
                .flatMapMany(dim -> dim.getValue(MeasurementParameter.of(request.getParams())))
                .map(val -> DashboardMeasurementResponse.of(request.getGroup(), val)));

        result.collectList().subscribe(data -> WebSocketService.postEvent(token, WebSocketEvent.REACTIVE, ReactiveResult.result(messageId, data)));

        return AjaxResult.success();
    }

    /**
     * 使用EventSource方式批量获取仪表数据,支持获取实时数据.
     *
     * @param requestJson 请求集合json
     * @return 仪表数据
     */
    @GetMapping(value = "/_multi", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public AjaxResult getMultiMeasurementValue(
            @RequestHeader("Message-ID") String messageId,
            @RequestParam String requestJson) {
        String token = getLoginUser().getToken();

        Flux<DashboardMeasurementResponse> result = Flux.fromIterable(JSON.parseArray(requestJson, DashboardMeasurementRequest.class))
                .flatMap(request -> dashboardManager
                        .getDashboard(request.getDashboard())
                        .flatMap(dash -> dash.getObject(request.getObject()))
                        .flatMap(obj -> obj.getMeasurement(request.getMeasurement()))
                        .flatMap(meas -> meas.getDimension(request.getDimension()))
                        .flatMapMany(dim -> dim.getValue(MeasurementParameter.of(request.getParams())))
                        .map(val -> DashboardMeasurementResponse.of(request.getGroup(), val)));

        result.collectList().subscribe(data -> WebSocketService.postEvent(token, WebSocketEvent.REACTIVE, ReactiveResult.result(messageId, data)));

        return AjaxResult.success();
    }
}
